#!/bin/bash 

# common function utils 
LOG_DEBUG=4
LOG_INFO=3
LOG_WARN=2
LOG_ERROR=1
LogLevel=$LOG_INFO

INFO() {
    if [ $LogLevel -ge $LOG_INFO ]; then 
        echo -e "\033[32m [Info] \033[0m: $1"
    fi 
}

ERROR() {
    if [ $LogLevel -ge $LOG_ERROR ]; then 
        echo -e "\033[31m [Error] \033[0m: $1"
        exit 1
    fi 
}

WARN() {
    if [ $LogLevel -ge $LOG_WARN ]; then 
        echo -e "\033[33m [Warn] \033[0m: $1"
    fi 
}

DEBUG() {
    if [ $LogLevel -ge $LOG_DEBUG ]; then 
        echo -e "\033[34m [Debug] \033[0m: $1"
    fi 
}

setLogLevel() {
    loglvl=$1
    if [ "$loglvl" == "info" ]; then 
        LogLevel=$LOG_INFO
    elif [ "$loglvl" == "debug" ]; then 
        LogLevel=$LOG_DEBUG
    elif [ "$loglvl" == "error" ]; then 
        LogLevel=$LOG_ERROR
    elif [ "$loglvl" == "warn" ]; then 
        LogLevel=$LOG_WARN
    fi 
}

checkArgsRequiredNums() {
    if [[ $1 -lt $2 ]]; then 
        ERROR "invalid args: args numbers required $2, but $1"
    fi 
}

checkArgsRequireNotNull() {
    if [[ $2 == "" ]] || [[ $2 == "null" ]]; then
        ERROR "invalid args: $1"
    fi  
}

toUpper() {
    echo $1 | tr 'a-z' 'A-Z'
}

toLower() {
    echo $1 | tr 'A-Z' 'a-z'
}

checkRequirement() {
    docker version > /dev/null 2>&1
    if [[ $? -ne 0 ]]; then 
        ERROR "docker not installed"
    fi 
    INFO "docker check ok"
    docker-compose version > /dev/null 2>&1
    if [[ $? -ne 0 ]]; then 
        ERROR "docker-compose not installed"
    fi 
    INFO "docker-compose check ok"
    jq --version > /dev/null 2>&1
    if [[ $? -ne 0 ]]; then 
        ERROR "jq not installed"
    fi 
    INFO "jq check ok"
    cryptogen version > /dev/null 2>&1
    if [[ $? -ne 0 ]]; then 
        ERROR "cryptogen not installed"
    fi 
    INFO "cryptogen check ok."
    cryptoVersion=$(cryptogen version | grep Version | awk -F': ' '{print $2}')
    if [[ $cryptoVersion != $global_version ]]; then 
        ERROR "cryptogen version not match, required $global_version, but $cryptoVersion"
    fi 
    configtxgenVersion=$(configtxgen -version | grep Version | awk -F': ' '{print $2}')
    if [[ $configtxgenVersion != $global_version ]]; then 
        ERROR "cryptogen version not match, required $global_version, but $configtxgenVersion"
    fi
}

## 全局变量
global_domain=""
global_repo="hyperledger"
global_version=""
global_network=""
global_basedir=".fabric-config"
global_orderertype=""

createConfigTxYAML() {
    echo ""
}
# 创建crypto.yaml配置文件
createCryptoYAML() {
    orgName=$1
    count=$2
    enableOUS=$3
    echo "- Name: $orgName
  Domain: $(toLower $orgName).$global_domain
  EnableNodeOUs: $enableOUS
  Template: 
    Count: $count" >> $global_basedir/crypto.yaml
}

initComposeYAML() {
    echo "version: '2'

networks:
    $global_network: 

services:" > $global_basedir/docker-compose.yaml

    echo "COMPOSE_PROJECT_NAME=fabric" > $global_basedir/.env
}

# 初始化fabric网络配置信息
g_network_config="{}"
initFabricNetworkConfig() {
    g_network_config=$(echo $g_network_config | jq .peerOrgs={})
    g_network_config=$(echo $g_network_config | jq .ordererOrg={})
}

# 创建peer.yaml配置文件
writePeerComposeYAML() {
    checkArgsRequiredNums $# 5
    peerId=$1; orgId=$(toLower $2); mspId=$3; grpcPort=$4; eventPort=$5
    id=$peerId.$orgId.$global_domain
    echo "
    $id:
        container_name: $id
        image: $global_repo/fabric-peer:$global_version
        environment:
            - CORE_PEER_ID=$peerId.$orgId
            - CORE_PEER_ADDRESS=$id:7051
            - CORE_PEER_LISTENADDRESS=0.0.0.0:7051
            - CORE_PEER_CHAINCODEADDRESS=$id:7052
            - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052
            - CORE_PEER_GOSSIP_EXTERNALENDPOINT=$id:7051
            - CORE_PEER_LOCALMSPID=$mspId
            - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
            - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=\${COMPOSE_PROJECT_NAME}_$global_network
            - FABRIC_LOGGING_SPEC=INFO
            - CORE_PEER_TLS_ENABLED=true
            - CORE_PEER_GOSSIP_USELEADERELECTION=true
            - CORE_PEER_GOSSIP_ORGLEADER=false
            - CORE_PEER_PROFILE_ENABLED=true
            - CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/fabric/msp
            - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
            - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
            - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
            - CORE_CHAINCODE_BUILDER=hyperledger/fabric-ccenv:${global_version}
            - CORE_CHAINCODE_GOLANG_RUNTIME=hyperledger/fabric-baseos:0.4.21
        volumes:
            - /var/run/:/host/var/run/
            - ./crypto-config/peerOrganizations/$orgId.$global_domain/peers/$id/msp:/etc/hyperledger/fabric/msp
            - ./crypto-config/peerOrganizations/$orgId.$global_domain/peers/$id/tls:/etc/hyperledger/fabric/tls
        ports:
            - $grpcPort:7051
            - $eventPort:7053
        networks:
            - $global_network" >> $global_basedir/docker-compose.yaml
}

writeOrdererComposeYAML() {
    checkArgsRequiredNums $# 5
    ordererId=$1; orgId=$(toLower $2); mspId=$3; ordererType=$4; gRPCPort=$5
    id=$ordererId.$orgId.$global_domain
    echo "
    $id:
        container_name: $id
        image: $global_repo/fabric-orderer:$global_version
        environment:
            - ORDERER_GENERAL_LOGLEVEL=DEBUG
            - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
            - ORDERER_GENERAL_GENESISMETHOD=file
            - ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/genesis.block
            - ORDERER_GENERAL_LOCALMSPID=$mspId
            - ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp
            - ORDERER_GENERAL_TLS_ENABLED=true
$(setOrdererTLS $ordererType)
        volumes:
            - ./channel-config/genesis.block:/var/hyperledger/orderer/genesis.block
            - ./crypto-config/ordererOrganizations/$orgId.$global_domain/orderers/$id/msp:/var/hyperledger/orderer/msp
            - ./crypto-config/ordererOrganizations/$orgId.$global_domain/orderers/$id/tls:/var/hyperledger/orderer/tls
        ports:
            - $gRPCPort:7050 
        networks:
            - $global_network" >> $global_basedir/docker-compose.yaml
} 

setOrdererTLS() {
    ordererType=$1
    echo -e "$gSpace12- ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key" | sed 's/#/ /g'
    echo -e "$gSpace12- ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt" | sed 's/#/ /g'
    echo -e "$gSpace12- ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]" | sed 's/#/ /g'
    if test $ordererType == "etcdraft"; then 
        echo -e "$gSpace12- ORDERER_GENERAL_CLUSTER_CLIENTCERTIFICATE=/var/hyperledger/orderer/tls/server.crt" | sed 's/#/ /g'
        echo -e "$gSpace12- ORDERER_GENERAL_CLUSTER_CLIENTPRIVATEKEY=/var/hyperledger/orderer/tls/server.key" | sed 's/#/ /g'
        echo -e "$gSpace12- ORDERER_GENERAL_CLUSTER_ROOTCAS=/var/hyperledger/orderer/tls/ca.crt" | sed 's/#/ /g'
    fi 
}

writeClientComposeYAML() {
    clientID=$global_network-client
    echo "
    $clientID:
        container_name: $clientID
        image: $global_repo/fabric-tools:$global_version
        tty: true
        stdin_open: true
        environment:
            - GOPATH=/opt/gopath
            - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
            - FABRIC_LOGGING_SPEC=INFO
            - CORE_PEER_TLS_ENABLED=true
        working_dir: /opt/gopath
        command: /bin/bash
        volumes:
            - /var/run/:/host/var/run/
            - ./chaincode:/opt/gopath/src/github.com/chaincode
            - ./crypto-config:/opt/config/crypto-config
            - ./channels:/opt/config/channels
        networks:
            - $global_network " >> $global_basedir/docker-compose.yaml
}

writeCAComposeYAML() {
    orgId=$1
    port=$2
    id=$orgId-ca.$global_domain
    # keyFile=$(cd $global_basedir/crypto-config/peerOrganizations/$orgId.$global_domain/ca; ls *_sk)
    echo "
    $id:
        container_name: $id
        image: $global_repo/fabric-ca:$global_version
        command: fabric-ca-server start -b admin:adminpw
        environment: 
            - FABRIC_CA_SERVER_HOME=/etc/hyperledger/fabric-ca-server
            - FABRIC_CA_SERVER_CA_CERTFILE=/tmp/crypto-ca/ca.$orgId.$global_domain-cert.pem
            - FABRIC_CA_SERVER_CA_KEYFILE=/tmp/crypto-ca/ca-key.pem
            - FABRIC_CA_SERVER_CFG_AFFILIATIONS_ALLOWREMOVE=true
            - FABRIC_CA_SERVER_CFG_IDENTITIES_ALLOWREMOVE=true
            - FABRIC_CA_SERVER_CFG_IDENTITIES_PASSWORDATTEMPTS=5
        ports:
            - $port:7054 
        volumes:
            - ./crypto-config/peerOrganizations/$orgId.$global_domain/ca:/tmp/crypto-ca
    " >> $global_basedir/docker-compose.yaml
}

writeFabricExplorererYAML() {
    network=$(cat $global_basedir/network-config.json | jq -r .name)
    echo "---
version: '2.1'
volumes:
  pgdata: 
  walletstore:
networks:
  explorer-net:
    external:
      name: fabric_$network

services:
  explorerdb.fabric.com:
    image: hyperledger/explorer-db:latest
    container_name: explorerdb.fabric.com
    hostname: explorerdb.fabric.com
    environment:
      - DATABASE_DATABASE=fabricexplorer
      - DATABASE_USERNAME=hppoc
      - DATABASE_PASSWORD=password
    healthcheck:
      test: \"pg_isready -h localhost -p 5432 -q -U postgres\"
      interval: 30s
      timeout: 10s
      retries: 5
    volumes:
      - pgdata:/var/lib/postgresql/data
    networks:
      - explorer-net

  explorer.fabric.com:
    image: hyperledger/explorer:1.1.0
    container_name: explorer.fabric.com
    hostname: explorer.fabric.com
    environment:
      - DATABASE_HOST=explorerdb.fabric.com
      - DATABASE_DATABASE=fabricexplorer
      - DATABASE_USERNAME=hppoc
      - DATABASE_PASSWD=password
      - LOG_LEVEL_APP=debug
      - LOG_LEVEL_DB=debug
      - LOG_LEVEL_CONSOLE=info
      - LOG_CONSOLE_STDOUT=true
      - DISCOVERY_AS_LOCALHOST=false
    volumes:
      - ./config.json:/opt/explorer/app/platform/fabric/config.json
      - ./connection-profile:/opt/explorer/app/platform/fabric/connection-profile
      - ../crypto-config:/tmp/crypto
      - walletstore:/opt/wallet
    command: sh -c \"node /opt/explorer/main.js && tail -f /dev/null\"
    ports:
      - 8090:8080
    depends_on:
      explorerdb.fabric.com:
        condition: service_healthy
    networks:
      - explorer-net
    " > $global_basedir/explorer/docker-compose.yaml
}

writeOUSConfig() {
    echo "NodeOUs:
  Enable: true
  ClientOUIdentifier:
    Certificate: cacerts/cacert.pem
    OrganizationalUnitIdentifier: client
  PeerOUIdentifier:
    Certificate: cacerts/cacert.pem
    OrganizationalUnitIdentifier: peer
  AdminOUIdentifier:
    Certificate: cacerts/cacert.pem
    OrganizationalUnitIdentifier: admin
  OrdererOUIdentifier:
    Certificate: cacerts/cacert.pem
    OrganizationalUnitIdentifier: orderer" > $1
}

gSpace8=$(echo "########")
gSpace12=$(echo "############")
gSpace16=$(echo "################")
gSpace20=$(echo "####################")

createConfigtxTemplate() {
    ordererType=$1
    echo -e "---
Organizations: 
$(getOrdererOrganizations)
$(getPeerOrganizations)

Capabilities:
    Channel: &ChannelCapabilities
        V1_4_3: true
        V1_3: false
        V1_1: false
    Orderer: &OrdererCapabilities
        V1_4_2: true
        V1_1: false
    Application: &ApplicationCapabilities
        V1_4_2: true
        V1_3: false
        V1_2: false
        V1_1: false
    
Application: &ApplicationDefaults
    Organizations:
    Policies:
        Readers:
            Type: ImplicitMeta
            Rule: \"ANY Readers\"
        Writers:
            Type: ImplicitMeta
            Rule: \"ANY Writers\"
        Admins:
            Type: ImplicitMeta
            Rule: \"MAJORITY Admins\"
    Capabilities:
        <<: *ApplicationCapabilities
    
Orderer: &OrdererDefaults
    OrdererType: $ordererType
    Addresses: 
$(getOrdererAddress 8 | sed 's/#/ /g')
    BatchTimeout: 2s
    BatchSize:
        MaxMessageCount: 10
        AbsoluteMaxBytes: 99 MB
        PreferredMaxBytes: 512 KB
    
    Kafka:
        Brokers:
            - 127.0.0.1:9092
    EtcdRaft:
        Consenters:
$(getOrdererConsenters 12 | sed 's/#/ /g')
    
    Organizations:
    Policies:
        Readers:
            Type: ImplicitMeta
            Rule: \"ANY Readers\"
        Writers:
            Type: ImplicitMeta
            Rule: \"ANY Writers\"
        Admins:
            Type: ImplicitMeta
            Rule: \"MAJORITY Admins\"
        BlockValidation:
            Type: ImplicitMeta
            Rule: \"ANY Writers\"

Channel: &ChannelDefaults
    Policies:
        Readers:
            Type: ImplicitMeta
            Rule: \"ANY Readers\"
        Writers:
            Type: ImplicitMeta
            Rule: \"ANY Writers\"
        Admins:
            Type: ImplicitMeta
            Rule: \"MAJORITY Admins\"
    Capabilities:
        <<: *ChannelCapabilities
    
Profiles:
    OrgsChannel:
        Consortium: SampleConsortium
        <<: *ChannelDefaults
        Application:
            <<: *ApplicationDefaults
            Organizations: 
$(cat $vConfigFile | jq .consortium.peerOrgs | jq -r 'keys[]' | awk '{print "'$gSpace16'- *"$0}' | sed 's/#/ /g')
            Capabilities:
                <<: *ApplicationCapabilities

    OrgsOrdererGenesis:
        <<: *ChannelDefaults
        Orderer:
            <<: *OrdererDefaults
            Organizations: 
$(cat $vConfigFile | jq -r .consortium.ordererOrg.name | awk '{print "'$gSpace16'- *"$0}' | sed 's/#/ /g')
            Capabilities:
                <<: *OrdererCapabilities
        Consortiums:
            SampleConsortium:
                Organizations: 
$(cat $vConfigFile | jq .consortium.peerOrgs | jq -r 'keys[]' | awk '{print "'$gSpace16'- *"$0}' | sed 's/#/ /g')
                
    MultiNodeEtcdRaft:
        <<: *ChannelDefaults
        Capabilities:
            <<: *ChannelCapabilities
        Orderer:
            <<: *OrdererDefaults
            OrdererType: etcdraft
            EtcdRaft:
                Consenters:
$(getOrdererConsenters 20 | sed 's/#/ /g')
            Addresses:
$(getOrdererAddress 16 | sed 's/#/ /g')
            Organizations:
$(cat $vConfigFile | jq -r .consortium.ordererOrg.name | awk '{print "'$gSpace16'- *"$0}' | sed 's/#/ /g')
            Capabilities:
                <<: *OrdererCapabilities
        Application:
            <<: *ApplicationDefaults
            Organizations:
$(cat $vConfigFile | jq -r .consortium.ordererOrg.name | awk '{print "'$gSpace16'- <<: *"$0}' | sed 's/#/ /g')
        Consortiums:
            SampleConsortium:
                Organizations:
$(cat $vConfigFile | jq .consortium.peerOrgs | jq -r 'keys[]' | awk '{print "'$gSpace20'- *"$0}' | sed 's/#/ /g')
" > $global_basedir/configtx.yaml
}

getOrdererOrganizations() {
    orgName=$(cat $vConfigFile | jq -r .consortium.ordererOrg.name)
    mspid=$(cat $vConfigFile | jq -r .consortium.ordererOrg.mspid)
    echo "- &$orgName
    Name: $orgName
    ID: $mspid
    MSPDir: crypto-config/ordererOrganizations/$(toLower $orgName).$global_domain/msp
    Policies:
        Readers:
            Type: Signature
            Rule: \"OR('$mspid.member')\"
        Writers:
            Type: Signature
            Rule: \"OR('$mspid.member')\"
        Admins:
            Type: Signature
            Rule: \"OR('$mspid.admin')\""
}

getPeerOrganizations() {
    config=$global_basedir/network-config.json 
    peerOrgCount=$(cat $vConfigFile | jq .consortium.peerOrgs | jq keys | jq length)
    for ((i=0; i<$peerOrgCount; i++)); do 
        orgName=$(cat $vConfigFile | jq -r .consortium.peerOrgs | jq -r 'keys'[$i])
        mspid=$(cat $vConfigFile | jq -r .consortium.peerOrgs.$orgName.mspid)
        anchorPeer=$(cat $config | jq -r .peerOrgs.\"$orgName\".peers.peer0.address)
        anchorPeerPort=$(cat $config | jq -r .peerOrgs.\"$orgName\".peers.peer0.grpc)
        echo "- &$orgName
    Name: $orgName
    ID: $mspid 
    MSPDir: crypto-config/peerOrganizations/$(toLower $orgName).$global_domain/msp
    Policies:
        Readers:
            Type: Signature
            Rule: \"OR('$mspid.admin', '$mspid.peer', '$mspid.client')\"
        Writers:
            Type: Signature
            Rule: \"OR('$mspid.admin', '$mspid.client')\"
        Admins:
            Type: Signature
            Rule: \"OR('$mspid.admin')\"
    AnchorPeers: 
        - Host: $anchorPeer
          Port: $anchorPeerPort"
    done 
}

getOrdererConsenters() {
    spaceNum=$1
    orgName=$(cat $vConfigFile | jq -r .consortium.ordererOrg.name)
    orgName=$(toLower $orgName)
    ordererCount=$(cat $vConfigFile | jq -r .consortium.ordererOrg.ordererCount)
    for ((i=0; i<$ordererCount; i++)); do 
        ordererID="orderer$i.$orgName.$global_domain"
        clientTlsCert=crypto-config/ordererOrganizations/$orgName.$global_domain/orderers/$ordererID/tls/server.crt
        serverTlsCert=crypto-config/ordererOrganizations/$orgName.$global_domain/orderers/$ordererID/tls/server.crt
        if test $spaceNum == 20; then 
            echo -e "$gSpace20- Host: $ordererID"
            echo -e "$gSpace20  Port: 7050"
            echo -e "$gSpace20  ClientTLSCert:" $clientTlsCert
            echo -e "$gSpace20  ServerTLSCert:" $serverTlsCert
        elif test $spaceNum == 16; then 
            echo -e "$gSpace16- Host: $ordererID"
            echo -e "$gSpace16  Port: 7050"
            echo -e "$gSpace16  ClientTLSCert:" $clientTlsCert
            echo -e "$gSpace16  ServerTLSCert:" $serverTlsCert
        elif test $spaceNum == 12; then 
            echo -e "$gSpace12- Host: $ordererID"
            echo -e "$gSpace12  Port: 7050"
            echo -e "$gSpace12  ClientTLSCert:" $clientTlsCert
            echo -e "$gSpace12  ServerTLSCert:" $serverTlsCert
        fi  
    done 
}

getOrdererAddress() {
    spaceNum=$1
    orgName=$(cat $vConfigFile | jq -r .consortium.ordererOrg.name)
    ordererCount=$(cat $vConfigFile | jq -r .consortium.ordererOrg.ordererCount)
    for ((i=0; i<$ordererCount; i++)); do 
        ordererID=$(getAddress orderer$i $orgName) 
        if test $spaceNum == 16; then 
            echo -e "$gSpace16- $ordererID:7050"
        elif test $spaceNum == 12; then 
            echo -e "$gSpace12- $ordererID:7050"
        elif test $spaceNum == 8; then 
            echo -e "$gSpace8- $ordererID:7050"
        fi 
    done 
}

getAddress() {
    echo $1.$(toLower $2).$global_domain
}

parseComposeConfig() {
    global_version=$(cat $vConfigFile | jq -r .version)
    global_repo=$(cat $vConfigFile | jq -r .repo)
    global_network=$(cat $vConfigFile | jq -r .name) 
    global_domain=$(cat $vConfigFile | jq -r .domain) 
    peerOrgCount=$(cat $vConfigFile | jq .consortium.peerOrgs | jq keys | jq length)

    if test -d $global_basedir; then 
        rm -rf $global_basedir
    fi 
    mkdir $global_basedir

    # 初始化
    initFabricNetworkConfig
    initComposeYAML 
    
    g_network_config=$(echo $g_network_config | jq .version=\"$global_version\")
    g_network_config=$(echo $g_network_config | jq .name=\"$global_network\")

    echo "OrdererOrgs: " >> $global_basedir/crypto.yaml 
    orgName=$(cat $vConfigFile | jq -r .consortium.ordererOrg.name)
    ordererCount=$(cat $vConfigFile | jq -r .consortium.ordererOrg.ordererCount)
    ordererMSP=$(cat $vConfigFile | jq -r .consortium.ordererOrg.mspid)
    ordererType=$(cat $vConfigFile | jq -r .consortium.ordererOrg.type)
    orgDomain=$(toLower $orgName).$global_domain
    createCryptoYAML $orgName $ordererCount false 
    g_network_config=$(echo $g_network_config | jq .ordererOrg.mspid=\"$ordererMSP\")
    g_network_config=$(echo $g_network_config | jq .ordererOrg.orderers={})
    g_network_config=$(echo $g_network_config | jq .ordererOrg.tlsca={})
    g_network_config=$(echo $g_network_config | jq .ordererOrg.domain=\"$orgDomain\")
    for ((i=0; i<$ordererCount; i++)); do 
        ordererId="orderer$i"
        gPort=$((7050 + i*100))
        ordererAddress=$(getAddress $ordererId $orgName)
        tlsrootCA=/opt/config/crypto-config/ordererOrganizations/$orgDomain/orderers/$ordererAddress/tls/ca.crt
        g_network_config=$(echo $g_network_config | jq .ordererOrg.orderers.\"$ordererAddress\"={})
        g_network_config=$(echo $g_network_config | jq .ordererOrg.orderers.\"$ordererAddress\".port=$gPort)
        g_network_config=$(echo $g_network_config | jq .ordererOrg.orderers.\"$ordererAddress\".tlsrootca=\"$tlsrootCA\")
        writeOrdererComposeYAML $ordererId $orgName $ordererMSP $ordererType $gPort
    done 
    echo "PeerOrgs: " >> $global_basedir/crypto.yaml 
    for ((i=0; i<$peerOrgCount; i++)); do 
        orgName=$(cat $vConfigFile | jq .consortium.peerOrgs | jq -r 'keys'[$i])
        peerCount=$(cat $vConfigFile | jq -r .consortium.peerOrgs.$orgName.peerCount)
        orgMSP=$(cat $vConfigFile | jq -r .consortium.peerOrgs.$orgName.mspid)
        enableCA=$(cat $vConfigFile | jq -r .consortium.peerOrgs.$orgName.enableCA)
        orgDomain=$(toLower $orgName).$global_domain
        g_network_config=$(echo $g_network_config | jq .peerOrgs.\"$orgName\"={})
        g_network_config=$(echo $g_network_config | jq .peerOrgs.\"$orgName\".peers={})
        g_network_config=$(echo $g_network_config | jq .peerOrgs.\"$orgName\".mspid=\"$orgMSP\")
        g_network_config=$(echo $g_network_config | jq .peerOrgs.\"$orgName\".domain=\"$orgDomain\")
        createCryptoYAML $orgName $peerCount true
        for ((j=0; j<$peerCount; j++)); do 
            peerId="peer$j"
            peerAddress=$peerId.$orgName.$global_domain
            gPort=$(( 7051 + i*1000 + j*100))
            ePort=$(( 7053 + i*1000 + j*100))
            tlsrootCA=/opt/config/crypto-config/peerOrganizations/$orgDomain/peers/$peerAddress/tls/ca.crt
            g_network_config=$(echo $g_network_config | jq .peerOrgs.\"$orgName\".peers.\"$peerId\"={})
            g_network_config=$(echo $g_network_config | jq .peerOrgs.\"$orgName\".peers.\"$peerId\".address=\"$peerAddress\")
            g_network_config=$(echo $g_network_config | jq .peerOrgs.\"$orgName\".peers.\"$peerId\".grpc=$gPort)
            g_network_config=$(echo $g_network_config | jq .peerOrgs.\"$orgName\".peers.\"$peerId\".event=$ePort)
            g_network_config=$(echo $g_network_config | jq .peerOrgs.\"$orgName\".peers.\"$peerId\".tlsrootca=\"$tlsrootCA\")
            writePeerComposeYAML $peerId $orgName $orgMSP $gPort $ePort
        done 
        if test $enableCA == true; then 
            caport=$(( 7054 + i*1000 ))
            g_network_config=$(echo $g_network_config | jq .peerOrgs.\"$orgName\".caspec={})
            g_network_config=$(echo $g_network_config | jq .peerOrgs.\"$orgName\".caspec.port=$caport)
            g_network_config=$(echo $g_network_config | jq .peerOrgs.\"$orgName\".caspec.address=\"localhost:$caport\")
            writeCAComposeYAML $orgName $caport 
        fi 
    done 
    writeClientComposeYAML
    
    echo $g_network_config | jq . > $global_basedir/network-config.json
    # init local directory 

    createConfigtxTemplate $ordererType

    mkdir $global_basedir/channels
    mkdir $global_basedir/chaincode 
}

generateFabricConfig() {
    INFO "Generate crypto certificates ..."
    cryptogen generate --config=$global_basedir/crypto.yaml --output=$global_basedir/crypto-config
    if test $? != 0; then 
        ERROR "Fail to generate crypto certificates"
    fi 
    INFO "Generate genesis block ..."
    ordererType=$(cat $vConfigFile | jq -r .consortium.ordererOrg.type)
    mkdir $global_basedir/channel-config
    if test $ordererType == "etcdraft"; then 
        configtxgen --configPath=$global_basedir --profile MultiNodeEtcdRaft -outputBlock $global_basedir/channel-config/genesis.block --channelID systemchain
    else 
        configtxgen --configPath=$global_basedir --profile OrgsOrdererGenesis -outputBlock $global_basedir/channel-config/genesis.block --channelID systemchain
    fi 
    if test $? != 0; then 
        ERROR "fail to generate genesis block!"
    fi 

    orgs=$(cat $global_basedir/network-config.json| jq .peerOrgs | jq -r 'keys[]')
    for org in ${orgs[@]}; do 
        domain=$(cat $global_basedir/network-config.json| jq -r .peerOrgs.\"$org\".domain)
        cp $global_basedir/crypto-config/peerOrganizations/$domain/ca/*_sk $global_basedir/crypto-config/peerOrganizations/$domain/ca/ca-key.pem
    done 
}

# gennerate fabric-network config 
generateFabricExplorerConfig() {
    output=$global_basedir/explorer
    if test ! -d $output; then 
        mkdir -p $output/connection-profile
    fi 
    config=$(echo {} | jq .\"network-configs\"={} | jq .license=\"Apache-2.0\")
    configFile=$global_basedir/network-config.json
    channels=$(cd $global_basedir/channels; ls)
    peerOrgs=$(cat $configFile | jq .peerOrgs | jq -r "keys[]")
    for org in ${peerOrgs[@]}; do 
        netConfig=$(echo {} | jq .name=\"$org-network\" | jq .version=\"1.0.0\" | jq .client={} | jq .channels={} | jq .organizations={} | jq .peers={})
        config=$(echo $config | jq .\"network-configs\".\"$org-config\"={})
        config=$(echo $config | jq .\"network-configs\".\"$org-config\".name=\"$org-network\")
        config=$(echo $config | jq .\"network-configs\".\"$org-config\".profile=\"./connection-profile/$org-network.json\")

        mspid=$(cat $configFile | jq -r .peerOrgs.\"$org\".mspid)
        domain=$(cat $configFile | jq -r .peerOrgs.\"$org\".domain)
        checkArgsRequireNotNull "mspid" $mspid
        # INFO "mspid: $mspid"
        keyfile=$(cd $global_basedir/crypto-config/peerOrganizations/$domain/users/Admin@$domain/msp/keystore; ls)
        orgAdminKeystore="/tmp/crypto/peerOrganizations/$domain/users/Admin@$domain/msp/keystore/$keyfile"
        orgAdminSigncert="/tmp/crypto/peerOrganizations/$domain/users/Admin@$domain/msp/signcerts/Admin@$domain-cert.pem"
        netConfig=$(echo $netConfig | jq .client.tlsEnable=true | jq .client.adminUser=\"admin\" | jq .client.adminPassword=\"\")
        netConfig=$(echo $netConfig | jq .client.enableAuthentication=false)
        netConfig=$(echo $netConfig | jq .client.organization=\"$mspid\")
        netConfig=$(echo $netConfig | jq .client.connection={} | jq .client.connection.timeout={} | jq .client.connection.timeout.peer={})
        netConfig=$(echo $netConfig | jq .client.connection.timeout.peer.endorser=300)
        netConfig=$(echo $netConfig | jq .client.connection.timeout.orderer=300)
        netConfig=$(echo $netConfig | jq .organizations.\"$mspid\"={})
        netConfig=$(echo $netConfig | jq .organizations.\"$mspid\".mspid=\"$mspid\")
        netConfig=$(echo $netConfig | jq .organizations.\"$mspid\".adminPrivateKey={})
        netConfig=$(echo $netConfig | jq .organizations.\"$mspid\".adminPrivateKey.path=\"$orgAdminKeystore\")
        netConfig=$(echo $netConfig | jq .organizations.\"$mspid\".signedCert={})
        netConfig=$(echo $netConfig | jq .organizations.\"$mspid\".signedCert.path=\"$orgAdminSigncert\")
        netConfig=$(echo $netConfig | jq .organizations.\"$mspid\".peers='[]')

        peers=$(cat $configFile | jq .peerOrgs.\"$org\".peers | jq -r "keys[]")

        for channel in ${channels[@]}; do 
            if test ! -f $global_basedir/channels/$channel/success; then 
                continue 
            fi 
            netConfig=$(echo $netConfig | jq .channels.\"$channel\"={} | jq .channels.\"$channel\".peers={} | jq .channels.\"$channel\".connection={})
            netConfig=$(echo $netConfig | jq .channels.\"$channel\".connection.timeout={} | jq .channels.\"$channel\".connection.timeout.peer={})
            netConfig=$(echo $netConfig | jq .channels.\"$channel\".connection.timeout.peer.endorser=\"6000\") 
            netConfig=$(echo $netConfig | jq .channels.\"$channel\".connection.timeout.peer.eventHub=\"6000\")
            netConfig=$(echo $netConfig | jq .channels.\"$channel\".connection.timeout.peer.eventReg=\"6000\")
            for peer in ${peers[@]}; do 
                address=$(cat $configFile | jq -r .peerOrgs.\"$org\".peers.\"$peer\".address)
                netConfig=$(echo $netConfig | jq .channels.\"$channel\".peers.\"$address\"={})
            done 
        done 

        # netConfig=$(echo $netConfig | jq .organizations.\"$mspid\".peers=[$peers])
        for peer in ${peers[@]}; do 
            #INFO "peer: $peer"
            address=$peer.$domain
            gprcPort=$(cat $configFile | jq -r .peerOrgs.\"$org\".peers.\"$peer\".grpc)
            eventPort=$(cat $configFile | jq -r .peerOrgs.\"$org\".peers.\"$peer\".event)
            peerTlsCA="/tmp/crypto/peerOrganizations/$domain/peers/$address/tls/ca.crt"
            netConfig=$(echo $netConfig | jq '.organizations.'$mspid'.peers += ['\"$address\"']')
            netConfig=$(echo $netConfig | jq .peers.\"$address\"={})
            netConfig=$(echo $netConfig | jq .peers.\"$address\".tlsCACerts={})
            netConfig=$(echo $netConfig | jq .peers.\"$address\".tlsCACerts.path=\"$peerTlsCA\")
            netConfig=$(echo $netConfig | jq .peers.\"$address\".url=\"grpcs://$address:7051\")
            netConfig=$(echo $netConfig | jq .peers.\"$address\".eventUrl=\"grpcs://$address:7053\")
            netConfig=$(echo $netConfig | jq .peers.\"$address\".grpcOptions={})
            netConfig=$(echo $netConfig | jq .peers.\"$address\".grpcOptions.\"ssl-target-name-override\"=\"$address\")
        done 
        echo $netConfig | jq . > $output/connection-profile/$org-network.json
    done 

    echo $config | jq . > $output/config.json

    writeFabricExplorererYAML 
    #echo $config | jq .
    
}

startFabricNetwork() {
    if test ! -d $global_basedir; then 
        ERROR "fabric network not init."
    fi 
    pushd $global_basedir
    docker-compose up -d 
    if test $? != 0; then 
        ERROR "fail to start fabric network." 
    fi 
    popd 
}

cleanFabricNetwork() {
    if test ! -d $global_basedir; then 
        return 
    fi 
    dockerpsa=$(docker ps -a | grep dev- | awk '{print $1}')
    for psa in ${dockerpsa[@]}; do 
        docker stop $psa 
        docker rm -f $psa 
    done 
    pushd $global_basedir
    docker-compose down -v 
    popd 
    # clean chaincode images
    docker volume prune -f
    rm -rf $global_basedir/{channels,channel-config,crypto-config,chaincode,.env}
    rm -f $global_basedir/{*.yaml,*.json} 
}

setupFabricExplorer() {
    if test ! -d $global_basedir/explorer; then 
        generateFabricExplorerConfig
    fi 
    if test $vUp == true; then 
        pushd $global_basedir/explorer
        docker-compose up -d 
        popd 
    fi 
    if test $vDown == true; then 
        pushd $global_basedir/explorer
        docker-compose down -v 
        popd 
        rm -rf $global_basedir/explorer
    fi 
}

g_orderer_endpoint=""
g_orderer_tlsca=""
g_peer_mspid=""
g_user_id=""
g_peer_address=""
g_org_domain=""
g_config=$global_basedir/network-config.json

initEnvironment() {
    orgID=$1
    peerID=$2
    config=$global_basedir/network-config.json
    g_orderer_endpoint=$(cat $config | jq .ordererOrg.orderers | jq -r "keys[0]")
    if [[ $g_orderer_endpoint == "" ]] || [[ $g_orderer_endpoint == "null" ]]; then 
        ERROR "missing orderer endpoint"
    fi 
    g_orderer_tlsca=$(cat $config | jq -r .ordererOrg.orderers.\"$g_orderer_endpoint\".tlsrootca)

    if test -z $peerID; then 
        peerID=$(cat $config| jq .peerOrgs.\"$orgID\".peers | jq -r "keys[0]")
        if [[ $peerID == "" ]] || [[ $peerID == "null" ]]; then 
            ERROR "missing peer"
        fi 
    fi 
    g_user_id=$vUser
    if test -z $vUser; then 
        g_user_id="Admin"
    fi
    g_peer_mspid=$(cat $config | jq -r .peerOrgs.\"$orgID\".mspid)
    if [[ $g_peer_mspid == "" ]] || [[ $g_peer_mspid == "null" ]]; then 
        ERROR "missing peer mspid"
    fi 
    g_peer_address=$(cat $config | jq -r .peerOrgs.\"$orgID\".peers.\"$peerID\".address)
    if [[ $g_peer_address == "" ]] || [[ $g_peer_address == "null" ]]; then 
        ERROR "missing peer address"
    fi 
    g_org_domain=$(cat $config | jq -r .peerOrgs.\"$orgID\".domain)
    if [[ $g_org_domain == "" ]] || [[ $g_org_domain == "null" ]]; then 
        ERROR "missing org domain"
    fi 
}

dockerClient() {
    cmd=$1  
    clientName=$(cat $global_basedir/network-config.json | jq -r .name) 
    baseDir=/opt/config/crypto-config/peerOrganizations/$g_org_domain
    # INFO "mspid: $g_peer_mspid"
    INFO "Command: \033[36;1m$cmd\033[0m"
    docker exec -t \
        -e CORE_PEER_LOCALMSPID=$g_peer_mspid \
        -e CORE_PEER_ADDRESS=$g_peer_address:7051 \
        -e CORE_PEER_MSPCONFIGPATH=$baseDir/users/$g_user_id@$g_org_domain/msp \
        -e CORE_PEER_TLS_CERT_FILE=$baseDir/peers/$g_peer_address/tls/server.crt \
        -e CORE_PEER_TLS_KEY_FILE=$baseDir/peers/$g_peer_address/tls/server.key \
        -e CORE_PEER_TLS_ROOTCERT_FILE=$baseDir/peers/$g_peer_address/tls/ca.crt \
        -t $clientName-client $cmd
    if test $? != 0; then 
        ERROR "Fail to execute command."
    fi 
}

channelCreate() {
    if test ! -d $global_basedir/crypto-config; then 
        ERROR "fabric network not init!"
    fi 
    if test -z $vOrg; then 
        ERROR "orgid is required"
    fi 
    channelDir=$global_basedir/channels/$vChannelID
    mkdir -p $channelDir
    INFO "configtxgen --configPath=$global_basedir --profile OrgsChannel -outputCreateChannelTx $channelDir/$vChannelID.tx --channelID $vChannelID"
    #configtxgen --configPath=$global_basedir --profile OrgsChannel -outputCreateChannelTx $channelDir/$vChannelID.tx --channelID $vChannelID
    ~/Downloads/bin/configtxgen --configPath=$global_basedir --profile OrgsChannel -outputCreateChannelTx $channelDir/$vChannelID.tx --channelID $vChannelID
    if test $? != 0; then 
        rm -rf $channelDir
        ERROR "fail to create channel config."
    fi 
    initEnvironment $vOrg 
    command="peer channel create -o $g_orderer_endpoint:7050 -c $vChannelID -f /opt/config/channels/$vChannelID/$vChannelID.tx --tls --cafile $g_orderer_tlsca"
    dockerClient "$command"
    touch $channelDir/success
}

updateAnchorPeer() {
    checkArgsRequireNotNull "orgid is required" $vOrg 
    checkArgsRequireNotNull "channelid is required" $vChannelID
    channelDir=/opt/config/channels/$vChannelID
    mspid=$(cat $global_basedir/network-config.json | jq -r .peerOrgs.\"$vOrg\".mspid)
    checkArgsRequireNotNull "$vOrg mspid is required" $mspid 
    pushd $global_basedir
    set -x 
    configtxgen --profile OrgsChannel -outputAnchorPeersUpdate channels/$vChannelID/${vOrg}MSPAnchor.tx -channelID $vChannelID -asOrg $vOrg
    set +x 
    popd
    if [[ $? -ne 0 ]]; then 
        ERROR "fail to create anchor file"
    fi  
    initEnvironment $vOrg 
    command="peer channel update -o $g_orderer_endpoint:7050 -c $vChannelID -f /opt/config/channels/$vChannelID/${vOrg}MSPAnchor.tx --tls --cafile $g_orderer_tlsca"
    dockerClient "$command"
}

joinChannel() {
    if test ! -d $global_basedir/crypto-config; then 
        ERROR "fabric network not init!"
    fi
    checkArgsRequireNotNull "channel is required" $vChannelID
    if test ! -d /opt/config/channels/$vChannelID; then 
        mkdir -p /opt/config/channels/$vChannelID
    fi 
    blockOutput=/opt/config/channels/$vChannelID/$vChannelID.block
    if test $vJoinAll == false; then 
        checkArgsRequireNotNull "orgID is required" $vOrg
        checkArgsRequireNotNull "peerID is required" $vPeer
        initEnvironment $vOrg $vPeer
        if test ! -f $global_basedir/channels/$vChannelID/$vChannelID.block; then 
            command="peer channel fetch 0 $blockOutput -o $g_orderer_endpoint:7050 -c $vChannelID --tls --cafile $g_orderer_tlsca"
            dockerClient "$command"
        fi  
        command="peer channel join -b $blockOutput"
        dockerClient "$command"
        return 0
    fi 
    
    config=$global_basedir/network-config.json 
    peerOrgs=$(cat $config | jq .peerOrgs | jq -r "keys[]")
    for org in ${peerOrgs[@]}; do 
        peers=$(cat $config | jq .peerOrgs.\"$org\".peers | jq -r "keys[]")
        for peer in ${peers[@]}; do 
            initEnvironment $org $peer 
            if test ! -f $global_basedir/channels/$vChannelID/$vChannelID.block; then 
                command="peer channel fetch 0 $blockOutput -o $g_orderer_endpoint:7050 -c $vChannelID --tls --cafile $g_orderer_tlsca"
                dockerClient "$command"
            fi
            command="peer channel join -b $blockOutput"
            dockerClient "$command"
        done 
    done 
}

installChaincode() {
    if test ! -d $global_basedir/crypto-config; then 
        ERROR "fabric network not init!"
    fi
    checkArgsRequireNotNull "chaincode name is required" $vCCName 
    checkArgsRequireNotNull "chaincode version is required" $vCCVersion
    checkArgsRequireNotNull "chaincode path is required" $vCCPath
    name=$vCCName
    version=$vCCVersion
    if test ! -d $global_basedir/chaincode/$name/$version; then 
        mkdir -p $global_basedir/chaincode/$name
        if test ! -d $vCCPath; then 
            ERROR "$vCCPath not exist."
        fi 
        cp -r $vCCPath $global_basedir/chaincode/$name/$version
    fi 
    if test $vLang == "golang"; then 
        path="github.com/chaincode/$name/$version"
    else
        path="/opt/gopath/src/github.com/chaincode/$name/$version"
    fi 
    if test $vJoinAll == false; then 
        checkArgsRequireNotNull "orgID is required" $vOrg
        checkArgsRequireNotNull "peerID is required" $vPeer
        initEnvironment $vOrg $vPeer 
        command="peer chaincode install -l $vLang -n $name -v $version -p $path"
        dockerClient "$command"
        return 0
    fi 
    config=$global_basedir/network-config.json 
    peerOrgs=$(cat $config | jq .peerOrgs | jq -r "keys[]")
    for org in ${peerOrgs[@]}; do 
        peers=$(cat $config | jq .peerOrgs.\"$org\".peers | jq -r "keys[]")
        for peer in ${peers[@]}; do 
            initEnvironment $org $peer 
            command="peer chaincode install -l $vLang -n $name -v $version -p $path"
            dockerClient "$command"
        done 
    done
}

# support fabric 2.0
installChaincodeV2() {
    if test ! -d $global_basedir/crypto-config; then 
        ERROR "fabric network not init!"
    fi
    checkArgsRequireNotNull "chaincode name is required" $vCCName 
    checkArgsRequireNotNull "chaincode version is required" $vCCVersion
    checkArgsRequireNotNull "chaincode path is required" $vCCPath
    name=$vCCName
    version=$vCCVersion
    if test ! -d $global_basedir/chaincode/$name/$version; then 
        mkdir -p $global_basedir/chaincode/$name
        if test ! -d $vCCPath; then 
            ERROR "$vCCPath not exist."
        fi 
        cp -r $vCCPath $global_basedir/chaincode/$name/$version
    fi 
    if test $vLang == "golang"; then 
        path="github.com/chaincode/$name/$version"
    else
        path="/opt/gopath/src/github.com/chaincode/$name/$version"
    fi 

    config=$global_basedir/network-config.json 
    peerOrgs=$(cat $config | jq .peerOrgs | jq -r "keys[]")
    for org in ${peerOrgs[@]}; do 
        peers=$(cat $config | jq .peerOrgs.\"$org\".peers | jq -r "keys[]")
        for peer in ${peers[@]}; do 
            initEnvironment $org $peer
            command="peer lifecycle chaincode package $name.tar.gz --path $vCCPath --lang $vLang --label $name"
            dockerClient "$command"
        done 
    done
}

# support by fabric 2.0
approveChaincode() {
    checkArgsRequireNotNull "chaincode name is required" $vCCName 
    checkArgsRequireNotNull "chaincode version is required" $vCCVersion
    checkArgsRequireNotNull "channelid is required" $vChannelID
    packageID=""
    initEnvironment $vOrg 
    command="peer lifecycle chaincode approveformyorg --channelID $vChannelID --name $vCCName --version $vCCVersion --init-required --package-id $packageID --sequence 1 --tls --cafile $g_orderer_tlsca"
    dockerClient "$command"
}

instantiateChaincode() {
    checkArgsRequireNotNull "channelid is required" $vChannelID
    checkArgsRequireNotNull "chaincode args is required" $vCCArgs
    checkArgsRequireNotNull "chaincode version is required" $vCCVersion
    checkArgsRequireNotNull "orgid is required" $vOrg
    initEnvironment $vOrg 
    if test -z $vPolicy; then 
        command="peer chaincode instantiate -o $g_orderer_endpoint:7050 -C $vChannelID -n $vCCName -v $vCCVersion --tls --cafile $g_orderer_tlsca -c $vCCArgs"
    else 
        command="peer chaincode instantiate -o $g_orderer_endpoint:7050 -C $vChannelID -n $vCCName -v $vCCVersion --tls --cafile $g_orderer_tlsca -c $vCCArgs -P $vPolicy" 
    fi 
    dockerClient "$command"
}

# support by fabric v2.0
commitChaincode() {
    command="peer lifecycle chaincode commit -o orderer0.orderer.example.com:7050 --channelID mychannel --name sample --version 1.0 --sequence 1 --init-required --tls true --cafile $CA_FILE --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $org1Tls --peerAddresses peer0.org2.example.com:7051 --tlsRootCertFiles $org2Tls"
    dockerClient "$command"
    command="chaincode invoke -o orderer0.orderer.example.com:7050 --isInit --tls true --cafile $CA_FILE -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER_ORG1_TLS --peerAddresses peer0.org2.example.com:7051 --tlsRootCertFiles $PEER_ORG2_TLS -c $vCCArgs --waitForEvent"
    dockerClient "$command"
}

invokeChaincode() {
    checkArgsRequireNotNull "channelid is required" $vChannelID
    checkArgsRequireNotNull "chaincode args is required" $vCCArgs
    checkArgsRequireNotNull "orgid is required" $vOrg
    initEnvironment $vOrg 
    command="peer chaincode invoke -o $g_orderer_endpoint:7050 -C $vChannelID -n $vCCName --tls --cafile $g_orderer_tlsca -c $vCCArgs"
    dockerClient "$command"
}

queryChaincode() {
    checkArgsRequireNotNull "channelid is required" $vChannelID
    checkArgsRequireNotNull "chaincode args is required" $vCCArgs
    checkArgsRequireNotNull "orgid is required" $vOrg
    initEnvironment $vOrg 
    command="peer chaincode query -o $g_orderer_endpoint:7050 -C $vChannelID -n $vCCName --tls --cafile $g_orderer_tlsca -c $vCCArgs"
    dockerClient "$command"
}

listAction() {
    checkArgsRequireNotNull "orgid is required" $vOrg 
    checkArgsRequireNotNull "peerid is required" $vPeer 
    if [[ $vChaincode == true ]]; then 
        initEnvironment $vOrg $vPeer 
        dockerClient "peer chaincode list --installed"
    fi 
    if [[ $vChannel == true ]]; then 
        initEnvironment $vOrg $vPeer 
        dockerClient "peer channel list"
    fi 
}

createUser() {
    checkArgsRequireNotNull "orgid is required" $vOrg 
    checkArgsRequireNotNull "username is required" $vUser
    domain=$(cat $global_basedir/network-config.json | jq -r .peerOrgs.\"$vOrg\".domain)
    caAddr=$(cat $global_basedir/network-config.json | jq -r .peerOrgs.\"$vOrg\".caspec.address)
    checkArgsRequireNotNull "domain ids required" $domain 
    certMgr=$global_basedir/crypto-config/peerOrganizations/$domain/cert-manager
    userCert=$global_basedir/crypto-config/peerOrganizations/$domain/users/$vUser@$domain
    if test -d $userCert; then 
        WARN "user [$vUser] has existed."
        return 0
    fi 
    if test ! -d $certMgr; then 
        mkdir $certMgr
        set -x
        fabric-ca-client enroll -u http://admin:adminpw@$caAddr -H $certMgr
        set +x
        if [[ $? -ne 0 ]]; then 
            ERROR "fail to init certmanager."
        fi 
    fi 
    fabric-ca-client affiliation list --affiliation=$domain -H $certMgr > /dev/null 2>&1
    if [[ $? -ne 0 ]]; then 
        fabric-ca-client affiliation add $domain -H $certMgr --force 
        if [[ $? -ne 0 ]]; then 
            ERROR "fail to add  affiliation $domain."
        fi 
    fi 
    fabric-ca-client identity list --id=$vUser -H $certMgr > /dev/null 2>&1
    if [[ $? -ne 0 ]]; then 
        fabric-ca-client register --id.name $vUser --id.secret $vSecret --id.type client --id.affiliation=$domain --id.attrs "role=client:ecert" -H $certMgr
        if [[ $? -ne 0 ]]; then 
            ERROR "register user fail."
        fi 
    fi 
    set -x
    fabric-ca-client enroll -u http://$vUser:$vSecret@$caAddr -H $userCert
    set +x
    if [[ $? -ne 0 ]]; then 
        ERROR "enroll certificate fail."
    fi 
    pushd $userCert
    mv msp/cacerts/*.pem msp/cacerts/cacert.pem 
    writeOUSConfig msp/config.yaml
    popd
    INFO "$vUser register success."
}

showActionHelp() {
    case $vAction in 
        deploy)
            echo -e "\033[35;1mExample:\033[0m 
    fabric-compose.sh deploy -f example.json"
            ;;
        start)
            echo -e "\033[35;1mExample:\033[0m
    fabric-compose.sh start"
            ;;
        stop)
            echo -e "\033[35;1mExample:\033[0m
    fabric-compose.sh stop"
            ;;
        clean)
            echo -e "\033[35;1mExample:\033[0m
    fabric-compose.sh clean"
            ;;
        create)
            echo -e "\033[35;1mExample:\033[0m
    fabric-compose.sh create --channelid=mychannel --org=org1."
            ;;
        join)
            echo -e "\033[35;1mExample:\033[0m
    fabric-compose.sh create --channelid=mychannel --org=org1 --peer=peer0
    fabric-compose.sh create --channelid=mychannel --all"
        ;;
        install)
            echo -e "\033[35;1mExample:\033[0m
    fabric-compose.sh install --name=mycc --version=0.1 --path=/path/to/chaincode --org=org1 --peer=peer0.
    fabric-compose.sh install --name=mycc --version=0.1 --path=/path/to/chaincode --all"
            ;;
        instantiate)
            echo -e "\033[35;1mExample:\033[0m
    fabric-compose.sh instantiate --name=mycc --version=0.1 --args=\"{\"Args\":[\"init\"]}\" --channelid=mychannel
    fabric-compose.sh instantiate --name=mycc --version=0.1 --args=\"{\"Args\":[\"init\"]}\" --policy=\"AND{'Org1MSP.peer','Org2MSP.peer}\" --channelid=mychannel."
            ;;
        invoke)
            echo -e "\033[35;1mExample:\033[0m
    fabric-compose.sh invoke --cc.name=mycc --cc.version=0.1 --args=\"{\"Args\":[\"init\"]}\" --channelid=mychannel"
            ;;
        list)
            echo -e "\033[35;1mExample:\033[0m 
    fabric-compose.sh list --channels --org=org1 --peer=peer0.
    fabric-compose.sh list --chaincodes --org=org1 --peer=peer0.\n"
            ;;
        register)
            echo -e "\033[35;1mExample:\033[0m
    fabric-compose.sh register --org=org1 --user=alice"
        ;;
    esac 
}

help() {
    if test -z $vAction; then 
        echo -e "Usage of fabric-compose.sh:
\033[37;1mDescription:\033[0m 
    fabric-compose is a tool for setup fabric network quickly, and support convennient operate command to acccess fabric network.
\033[37;1mCOMMAND:\033[0m
    fabric-compose.sh [action] [option]
\033[37;1mACTION:\033[0m
    deploy:       deploy fabric network by config.
    init:         prepare fabric network requirements.
    start:        start fabric network.
    stop:         stop fabric netork.
    clean:        stop fabric network and clean all resource.
    create:       create channel.
    install:      install chaincode.
    instantiate:  instantiate chaincode.
    upgrade:      upgrade chaincode.
    invoke:       invoke chaincode.
    query:        query chaincode.
\033[37;1mOPTION:\033[0m
    --all          :  All reasource that has been operated, such as 'join', 'install', 'clean'.
    --config    -f :  Configuration of fabric network.
    --channelid -c :  Channel id.
    --org          :  ID of peer organization.
    --peer         :  Peer id of peer organization, such as 'peer0'"
    else 
       showActionHelp 
    fi 
}

versionInfo() {
    echo -e "version: 1.0.0
Support: 1.4.x"
}

vConfigFile=""
vAction=""
vResource=""
vChannelID=""
vPeer=""
vUser=""
vOrg=""
vOrdererTarget=""
vJoinAll=false 
vCCName=""
vCCVersion=""
vCCPath=""
vCCArgs=""
vPolicy=""
vLang="golang"
vChaincode=false 
vChannel=false 
vUp=false 
vDown=false 
vUser=""
vSecret="123456"

while [ $# -ge 1 ]; do
    args=$1
    key=$(echo $args | awk -F'=' '{print $1}')
    value=$(echo $args | awk -F'=' '{print $2}')
    case "$key" in 
        deploy) vAction=deploy; shift 1;;
        init) vAction=init; shift 1;;
        start) vAction=start; shift 1;;
        stop) vAction=stop; shift 1;;
        clean) vAction=clean; shift 1;;
        channel) vResource="channel"; shift 1;;
        chaincode) vResource="chaincode"; shift 1;;
        create) vAction=create; shift 1;;
        join) vAction=join; shift 1;;
        list) vAction=list; shift 1;;
        update) vAction=update; shift 1;;
        install) vAction=install; shift 1;;
        instantiate) vAction=instantiate; shift 1;;
        invoke) vAction=invoke; shift 1;;
        query) vAction=query; shift 1;;
        version) versionInfo; exit 0;;
        explorer) vAction=explorer; shift 1;;
        register) vAction=$key; shift 1;;
        template) vAction=$key; shift 1;;
        -f) vConfigFile=$2; shift 2;;
        --config) vConfigFile=$value; shift 1;;
        -c) vChannelID=$2; shift 2;;
        --channelid) vChannelID=$value; shift 1;;
        --org) vOrg=$value; shift 1;;
        --peer) vPeer=$value; shift 1;;
        --all) vJoinAll=true; shift 1;;
        -n) vCCName=$2; shift 2;;
        --name) vCCName=$value; shift 1;;
        -v) vCCVersion=$2; shift 2;;
        --version) vCCVersion=$value; shift 1;;
        -a) vCCArgs=$2; shift 2;;
        --args) vCCArgs=$value; shift 1;;
        --policy) vPolicy=$value; shift 1;;
        --path) vCCPath=$value; shift 1;;
        --chaincodes) vChaincode=true; shift 1;;
        --channels) vChannel=true; shift 1;;
        --up) vUp=true; shift 1;;
        --down) vDown=true; shift 1;;
        --user) vUser=$value; shift 1;;
        --secret) vSecret=$value; shift 1;;
        -h) help; exit 0;;
        --help) help; exit 0;;
        *) ERROR "unknown args: $key"; exit 1;;
    esac
done 

case $vAction in
    deploy)
        checkArgsRequireNotNull "config file is required" $vConfigFile
        global_version=$(cat $vConfigFile | jq -r .version)
        checkRequirement
        parseComposeConfig
        generateFabricConfig
        sleep 0.5
        startFabricNetwork
        ;;
    init)
        checkArgsRequireNotNull "config file is required" $vConfigFile
        global_version=$(cat $vConfigFile | jq -r .version)
        checkRequirement
        parseComposeConfig
        generateFabricConfig
        ;;
    start)
        startFabricNetwork
        ;;
    stop)
        if test -d $global_basedir; then 
            pushd $global_basedir
            docker-compose down -v
            popd 
        else 
            INFO "$global_basedir not exist"
        fi  
        ;;
    clean)
        cleanFabricNetwork
        ;;
    create)
        channelCreate
        ;;
    join)
        joinChannel
        ;;
    update)
        updateAnchorPeer
        ;;
    install)
        installChaincode 
        ;;
    instantiate)
        instantiateChaincode
        ;;
    invoke)
        invokeChaincode
        ;;
    query)
        queryChaincode
        ;;
    list)
        listAction
        ;;
    register)
        createUser
        ;;
    explorer)
        setupFabricExplorer
        ;;
    template)
        showTemplate
        ;;
esac 
